home *** CD-ROM | disk | FTP | other *** search
- //--------------------------------------------------------------------------
- //
- // Copyright (c) 2002, Colin Granville
- //
- // All rights reserved.
- //
- // Redistribution and use in source and binary forms, with or
- // without modification, are permitted provided that the following
- // conditions are met:
- //
- // * Redistributions of source code must retain the above copyright
- // notice, this list of conditions and the following disclaimer.
- //
- // * Redistributions in binary form must reproduce the above
- // copyright notice, this list of conditions and the following
- // disclaimer in the documentation and/or other materials
- // provided with the distribution.
- //
- // * The name Colin Granville may not be used to endorse or promote
- // products derived from this software without specific prior
- // written permission.
- //
- // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- // COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
- // OF THE POSSIBILITY OF SUCH DAMAGE.
- //
- //--------------------------------------------------------------------------
-
- #include "guilib:gfx.h"
- #include "outline.h"
- #include "PDFDoc.h"
- #include "Catalog.h"
- #include "iostream.h"
- #include <stdlib.h>
- #include <stdarg.h>
- #include "object.h"
- #include "xref.h"
- #include "link.h"
- #include "stl:string.h"
- #include "DocView.h"
- #include "GuiIcon.h"
- #include "GuiTask.h"
- #include "Document.h"
- #include "GuiHourglass.h"
- #include "DrawOutputFont.h"
-
- #define SPRITEOUTDENT 28
- inline int levelIndent(int level) {return level*52;}
-
-
- LinkAction* makeAction(Dict *dict, GString *baseURI);
-
- class OutlineFont
- {
- public:
- OutlineFont();
- ~OutlineFont();
- bool isOk() {return handle;}
- private:
- int handle;
- };
-
- //******************************************************************************
-
- OutlineFont::OutlineFont()
- : handle(0)
- {
- _swix(Font_FindFont,_INR(1,5)|_OUT(0),"Homerton.Medium",10*16,10*16,0,0,&handle);
- }
-
- //******************************************************************************
-
- OutlineFont::~OutlineFont()
- {
- if (handle) _swix(Font_LoseFont,_IN(0),handle);
- }
-
- //******************************************************************************
- //******************************************************************************
- //******************************************************************************
-
- class OutlineNode
- {
- public:
- OutlineNode(OutlineNode* parent_,Object* o);
- ~OutlineNode();
- OutlineNode* getParent() {return parent;}
- void setParent(OutlineNode* n) {parent=n;}
- OutlineNode* getNext() {return next;}
- void setNext(OutlineNode* n) {next=n;}
- OutlineNode* getFirst() {return first;}
- void setFirst(OutlineNode* n) {first=n;}
- int getCount() {return count;}
- void setCount(int n) {count=n;}
- const string& getText() const {return text;}
- Object* getObject() {return ob;}
-
-
- private:
- OutlineNode* parent;
- OutlineNode* first;
- OutlineNode* next;
- int count;
- Object* ob;
- string text;
- };
-
- OutlineNode::OutlineNode(OutlineNode* parent_,Object* o)
- : parent(parent_),
- first(0),
- next(0),
- count(0),
- ob(o)
- {
- Object t;
- o->dictLookup("Title",&t);
- if (t.isString()) text=toAcornLatin1(t.getString()->getCString(),
- t.getString()->getLength());
-
- t.free();
- }
-
- //******************************************************************************
-
- OutlineNode::~OutlineNode()
- {
- delete first;
- delete next;
- if (ob) ob->free();
- delete ob;
- }
-
- //******************************************************************************
- //******************************************************************************
- //******************************************************************************
-
- Outline::Outline(PDFDoc& doc)
- : window("Outline"),
- owner(0),
- root(0),
- pdfDoc(0),
- height(28),
- redrawWindowTarget(&window,GuiWimp_ERedrawWindow,this,Outline::redrawWindow),
- mouseClickTarget(&window,GuiWimp_EMouseClick,this,Outline::mouseClick)
- {
- if (!doc.getXRef()) return;
-
- Object catalog;
- doc.getXRef()->getCatalog(&catalog);
- if (!catalog.isDict()) {catalog.free();return;}
-
- Object outlines;
- catalog.dictLookup("Outlines",&outlines);
- if (outlines.isDict())
- {
- Object ob;
- int count=0;
- outlines.dictLookup("First",&ob);
- if (ob.isDict()) count++;
- ob.free();
- outlines.dictLookup("Next",&ob);
- if (ob.isDict()) count++;
- ob.free();
- if (count>0) pdfDoc=&doc;
- }
- outlines.free();
- catalog.free();
- }
-
-
- //******************************************************************************
-
- Outline::~Outline()
- {
- delete root;
- }
-
- //******************************************************************************
-
- OutlineNode* Outline::fillTree(OutlineNode*parent,Object* entry )
- {
- if (!entry) return 0;
- OutlineNode* node = new OutlineNode(parent,entry);
- if (!node) return 0;
-
- Object* nextOb=new Object;
- if (nextOb)
- {
- entry->dictLookup("Next",nextOb);
- if (!nextOb->isDict())
- {
- nextOb->free();
- delete nextOb;
- nextOb=0;
- }
- }
-
- Object* firstOb=new Object;
- if (firstOb)
- {
- entry->dictLookup("First",firstOb);
- if (!firstOb->isDict())
- {
- firstOb->free();
- delete firstOb;
- firstOb=0;
- }
- }
-
- int count=0;
- {
- Object ob;
- entry->dictLookup("Count",&ob);
- if (ob.isInt()) count=ob.getInt();
- ob.free();
- }
-
- node->setCount(count);
- node->setFirst(fillTree(node,firstOb));
- node->setNext(fillTree(parent,nextOb));
- return node;
- }
-
- //******************************************************************************
-
- // Font must be set before use
- bool Outline::print(OutlineNode* node,int level)
- {
- while (node)
- {
- if (node->getText().size())
- {
- basey-=height;
- int y=redrawBlock->yToScreen(basey);
- if (y+height <= redrawBlock->redrawArea.ymin) return 1;
- if (y < redrawBlock->redrawArea.ymax)
- {
- OutlineNode*n=node;
- gfx::gcol_bgr(0,0xdadada00);
- int x=redrawBlock->xToScreen(basex);
- gfx::move(x+levelIndent(level)-SPRITEOUTDENT+8,y+height/2);
- gfx::drawby(SPRITEOUTDENT-12,0);
- for (int i=level;i>0;i--,n=n->getParent())
- {
- int extra=(n->getNext()?0:height/2);
- if (extra && i!=level) continue;
- gfx::move(x+levelIndent(i)-SPRITEOUTDENT+8,y+extra);
- gfx::drawby(0,height-extra);
- }
-
- int left=basex+levelIndent(level);
- if (node->getCount())
- {
- GuiIcon icon;
- icon.flags = GuiIcon::Sprite |
- GuiIcon::Indirected;
-
-
- icon.data.is.spriteNameLength = 12;
- icon.data.is.spriteArea = (void*) guiTask().spriteArea();
- icon.data.is.sprite = (node->getCount()<0 ? "plus":"minus");
- icon.bbox(left-SPRITEOUTDENT,basey+height/4,left,basey+height+height/4);
- icon.plot();
-
- }
-
- _swix(ColourTrans_SetFontColours,_INR(0,3),0,0xFFFFFF00,0,14);
- _swix(Font_Paint,_INR(0,7),0,node->getText().c_str(),0,
- redrawBlock->xToScreen(left)*400,y*400+height*400*1/4,0,0,0);
- }
- }
-
- if (node->getCount() > 0) if (print(node->getFirst(),level+1)) return 1;
- node=node->getNext();
- }
- return 0;
- }
-
- //******************************************************************************
-
- bool Outline::find(OutlineNode* node,int level)
- {
- for (;node;node=node->getNext())
- {
- if (node->getText().size())
- {
- basey-=height;
- if (findy >=basey && findy < basey+height )
- {
- foundNode=node;
- foundLevel=level;
- return 1;
- }
- }
-
- if (node->getCount() > 0 && find(node->getFirst(),level+1)) return 1;
- }
- return 0;
- }
-
- //******************************************************************************
-
- int Outline::countLines(OutlineNode* node)
- {
- int count=0;
- for (;node;node=node->getNext())
- {
- if (node->getText().size()) count++;
- if (node->getCount() > 0) count+=countLines(node->getFirst());
- }
- return count;
- }
-
- //******************************************************************************
-
- // returned width is in millipoints
- // Font must be set before use
- int getMaxWidth(OutlineNode* node,int level=0)
- {
- int width=0;
- for (;node;node=node->getNext())
- {
- if (node->getText().size())
- {
- int wid;
- _swix(Font_ScanString,_INR(1,4) | _OUT(3),node->getText().c_str(),0,0x80000000,0x80000000,&wid);
- wid+=(levelIndent(level)*400);
- if (wid>width) width=wid;
- }
-
- if (node->getCount() != 0)
- {
- int wid=getMaxWidth(node->getFirst(),level+1);
- if (wid>width) width=wid;
- }
- }
- return width;
- }
-
- //******************************************************************************
-
- void Outline::open(DocView& view)
- {
- if (owner!=&view) close(view);
- owner=&view;
-
- if (!isOk()) return;
-
- if (!root)
- {
- Object catalog;
- pdfDoc->getXRef()->getCatalog(&catalog);
- if (!catalog.isDict()) {catalog.free();return;}
-
- Object* outlines=new Object;
- if (outlines)
- {
- catalog.dictLookup("Outlines",outlines);
- if (!outlines->isDict())
- {
- outlines->free();
- delete outlines;
- outlines=0;
- }
- }
- catalog.free();
-
- {
- GuiHourglass hg;
- root=fillTree(0,outlines);
- }
-
- if (!root)
- {
- if (outlines) outlines->free();
- delete outlines;
- }
- else
- {
- GuiBBox box;
- window.getExtent(box);
- int ext=countLines(root)*height;
- if (ext==0)
- {
- delete root;
- root=0;
- }
- else
- {
- box.ymin=-ext;
- OutlineFont font;
- box.xmax=(getMaxWidth(root)/400)+16;
- window.setExtent(box);
- }
- }
- if (!root) pdfDoc=0;
- }
- if (isOk()) window.showAsMenuAtPointer(view.getWindow());
- }
-
- //******************************************************************************
-
- void Outline::close(DocView& view)
- {
- if (owner==&view)
- {
- window.hide();
- owner=0;
- }
- }
-
- //******************************************************************************
-
- Claim Outline::redrawWindow(GuiWimpPollBlock& wpb,const GuiIdBlock&)
- {
- bool more;
- GuiRedrawWindowBlock& block=wpb.redrawWindowRequest;
-
- for (GuiWindow::redraw(block,more);more;GuiWindow::getRectangle(block,more))
- {
- basey=0;
- basex=0;
- redrawBlock=█
- OutlineFont font;
- if (font.isOk()) print(root);
- }
-
- return CLAIM;
- }
-
- //*************************************************************************
-
- Claim Outline::mouseClick(GuiWimpPollBlock& wpb,const GuiIdBlock&)
- {
- if (wpb.mouseClick.buttons & GuiPointerInfo::Menu) return CLAIM;
-
- GuiGetWindowStateBlock ws;
- window.getState(ws);
- int x=ws.xToWorkarea(wpb.mouseClick.x);
-
- findy=ws.yToWorkarea(wpb.mouseClick.y);
- basey=0;
- if (find(root))
- {
- if (foundNode->getCount())
- {
- int x=ws.xToWorkarea(wpb.mouseClick.x);
- if (x >= levelIndent(foundLevel)-SPRITEOUTDENT-4 &&
- x < levelIndent(foundLevel))
- {
- foundNode->setCount(-foundNode->getCount());
-
- if (wpb.mouseClick.buttons & GuiPointerInfo::Adjust)
- {
- int close=(foundNode->getCount()<0);
- while (foundNode->getNext())
- {
- foundNode=foundNode->getNext();
- if ((close && foundNode->getCount()>0) ||
- (!close && foundNode->getCount()<0)
- ) foundNode->setCount(-foundNode->getCount());
-
- }
- }
-
- GuiBBox box;
- window.getExtent(box);
- int ext=countLines(root)*height;
- if (ext<ws.visibleArea.getHeight()) ext=ws.visibleArea.getHeight();
- box.ymin=-ext;
- window.setExtent(box);
- int bottom=-ws.yToWorkarea(ws.visibleArea.ymin);
- if (bottom>ext)
- {
- ws.yscroll+=(bottom-ext);
- window.showAsMenu(ws,owner->getWindow());
- }
-
- box.ymax=basey+height;
- window.forceRedraw(box);
- return CLAIM;
- }
- }
-
- { // limit clicks to text
- int wid=0;
- OutlineFont font;
- if (font.isOk()) _swix(Font_ScanString,_INR(0,5) | _OUT(3), 0,foundNode->getText().c_str(),0,
- -1,-1,0,&wid);
- if (x < levelIndent(foundLevel) || x > (levelIndent(foundLevel)+wid/400))
- {
- if (owner) close(*owner);
- return CLAIM;
- }
- }
-
- if (owner)
- {
- LinkAction* action=makeAction(foundNode->getObject()->getDict(),
- owner->getDocument().getCatalog()->getBaseURI());
-
- if (action)
- {
- owner->doAction(action,wpb.mouseClick.buttons & GuiPointerInfo::Adjust);
- delete action;
- }
- close(*owner);
- }
- }
- else
- {
- if (owner) close(*owner);
- }
- return CLAIM;
- }
-
- //******************************************************************************
- //******************************************************************************
- //******************************************************************************
-
- LinkAction* makeAction(Dict *dict, GString *baseURI)
- {
- Object obj1;
- LinkAction* action=0;
-
- // look for destination
- if (!dict->lookup("Dest", &obj1)->isNull()) {
- action = new LinkGoTo(&obj1);
-
- // look for action
- } else {
- Object obj2;
- obj1.free();
- if (dict->lookup("A", &obj1)->isDict()) {
- obj1.dictLookup("S", &obj2);
- Object obj3,obj4;
-
- // GoTo action
- if (obj2.isName("GoTo")) {
- obj1.dictLookup("D", &obj3);
- action = new LinkGoTo(&obj3);
- obj3.free();
-
- // GoToR action
- } else if (obj2.isName("GoToR")) {
- obj1.dictLookup("F", &obj3);
- obj1.dictLookup("D", &obj4);
- action = new LinkGoToR(&obj3, &obj4);
- obj3.free();
- obj4.free();
-
- // Launch action
- } else if (obj2.isName("Launch")) {
- action = new LinkLaunch(&obj1);
-
- // URI action
- } else if (obj2.isName("URI")) {
- obj1.dictLookup("URI", &obj3);
- action = new LinkURI(&obj3, baseURI);
- obj3.free();
-
- // Named action
- } else if (obj2.isName("Named")) {
- obj1.dictLookup("N", &obj3);
- action = new LinkNamed(&obj3);
- obj3.free();
-
- // unknown action
- } else if (obj2.isName()) {
- action = new LinkUnknown(obj2.getName());
-
- // action is missing or wrong type
- } else {
- //error(-1, "Bad annotation action");
- action = NULL;
- }
-
- obj2.free();
-
- }
- }
- obj1.free();
- return action;
- }
-
- //****************************************************************
- //****************************************************************
- //****************************************************************
-
-